[Ruby on Rails]FTPにて正しくファイルが取得できたかを確認する – MD5による検証
はじめに
今回はFTPサーバより取得したファイルが、正しくダウンロードできているのか検証する方法についてです。結論から書くとMD5チェックサムを使い、FTPサーバ上に用意したチェックサムと、ダウンロードしたファイルから算出したMD5値が一致するかで検証します。まあ、昔からある非常に古典的なやり方です。
処理概要
タイトルにもある通り、今回のサンプルはRuby on Railsを使用しました。処理の概要としては、以下の通りとなります。
- FTPサーバ内には、ダウンロード対象のファイルと、そのファイルより算出したMD5の値(チェックサム)を記述したファイルを用意する。
- Railsアプリは、FTPサーバに用意したファイルを取得する。
- 取得後、ダウンロードしたファイルから算出したMD5の値と、チェックサムを記述したファイル内の値を比較する。
FTPサーバ
では、FTPサーバで行う手順についてです。FTPサーバ内にダウンロード対象のファイルを用意した後、チェックサムを記述したファイルを用意します。
まずFTPサーバを起動します。Macの場合は以下のコマンドとなります。
$ sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist
FTPサーバの任意のフォルダに、ダウンロードしたファイルを用意します。今回は「sample1.dmg」「sample2.dmg」の2ファイルを用意しました。
その後、以下のコマンドでファイルのMD5を算出し、チェックサムを記述したファイルを用意します。
$ md5 * >> large.md5sum
「md5」コマンドは、Macの場合のコマンドです。「*」を指定することで、フォルダ内の全ファイルのMD5を算出しています。パイプで、算出した結果をファイルに出力しています。この結果、以下のようなチェックサムが出力されました。
MD5 (sample1.dmg) = 2995f70a1779dc28b97be7f4b98f0719 MD5 (sample2.dmg) = 4796db9cc3a928cf477f6c3f1cde0ea4
見ての通り「MD5 (ファイル名) = MD5値」という形式で出力されています。
サンプルソース
最後に、Railsでのサンプルソースです。一番上の「self.exec」メソッドが、エントリーポイントとなります。
尚、各パスはsettingslogicを使いymlファイル内に定義しているため、「Settings〜」という記述となっています。
require 'net/ftp' require 'digest/md5' class FtpMd5 def self.exec download check_md5 end private def self.check_md5 md5sum = read_md5sum Dir.glob(download_folder + "/*.dmg") {|file| download_md5 = Digest::MD5.hexdigest(File.open(file, 'rb').read) original_md5 = md5sum[File.basename(file)] return false if download_md5 != original_md5 } true end def self.read_md5sum result = Hash.new reg = Regexp.new('MD5 \((.*)\)') File.open(File.join(download_folder, Settings.download.md5sum_file), "r") {|file| file.each {|line| content = line.split(' = ') match = reg.match(content[0]) result[match[1]] = content[1].chomp } } result end def self.download Net::FTP.open(Settings.download.host) do |ftp| ftp.login(Settings.download.user, Settings.download.password) ftp.passive = true ftp.chdir(Settings.download.remote_large_folder) ftp.list('*.dmg').each {|file| file_name = file.match(/[^\s]*\.dmg/).to_s ftp.getbinaryfile(file_name, File.join(download_folder, file_name)) } ftp.gettextfile(Settings.download.md5sum_file, File.join(download_folder, Settings.download.md5sum_file)) end end def self.download_folder File.join(Rails.root, Settings.download.local_folder) end end
「self.exec」メソッド内では最初に「download」メソッドを呼び、FTPサーバよりファイルをダウンロードしています。次に「check_md5」メソッドを呼び、MD5の比較にて正しくファイルが取得できたかを確認しています。
「download」メソッドでは、特に変わった事は行っておりません。「Net::FTP」を使い、FTPにてファイルをダウンロードしています。.dmgファイルはバイナリなので「getbinaryfile」を(44行目)、MD5チェックサムファイルはテキストなので「gettextfile」(46行目)を使用しています。
「check_md5」メソッドでは「Digest::MD5.hexdigest」を使い、ダウンロードしたファイルの中身からMD5値を生成しています(16行目)。今回は一括でファイルを読み込んでいますが、ファイルの容量が多い場合は分割したほうが良いようです。ちなみに1.2Gのファイルを用いましたが、私の環境では特に問題はなかったです。18行目でダウンロードしたファイルから生成したMD5値と、FTPサーバに予め用意したチェックサムを比較し、異なるようならfalseを返却しています。
まとめ
ダウンロード対象のファイルのチェックサムを予め作っておくことで、ダウンロードが正常に出来たかを確認することができました。今回はダウンロード直後にチェックを行いましたが、さらに別箇所に転送する場合など、案件に合わせて任意のタイミングでチェックすることもできるかと思います。何かのときに、参考になれば幸いです。